home *** CD-ROM | disk | FTP | other *** search
/ PC World 2007 June / PCWorld_2007-06_cd.bin / v cisle / tclock / tclocklight-040702-3.exe / source / sntp / sntp.c < prev    next >
C/C++ Source or Header  |  2004-04-01  |  16KB  |  598 lines

  1. /*-------------------------------------------------------------
  2.   sntp.c : SNTP client
  3.   (C) 1997-2003 Kazuto Sato
  4.   Please read readme.txt about the license.
  5.   
  6.   Written by Kazubon, Nanashi-san
  7.   Special thanks to Tomoaki Nakashima
  8. ---------------------------------------------------------------*/
  9.  
  10. #include "tcsntp.h"
  11.  
  12. #include <winsock.h>
  13. #include <ras.h>
  14.  
  15. /* Globals */
  16.  
  17. BOOL InitSNTP(HWND hwndParent);
  18. void EndSNTP(HWND hwndParent);
  19. void SNTPCommand(HWND hwndMain, const char *pCommand);
  20. void SetSNTPParam(const char *servername, int nTimeout, BOOL bLog,
  21.     const char *soundfile);
  22. BOOL StartSyncTime(HWND hwnd, const char *pServer, BOOL bRAS);
  23. void OnTimerSNTP(HWND hwndMain);
  24. void OnGetHost(HWND hwnd, WPARAM wParam, LPARAM lParam);
  25. void OnReceive(HWND hwnd, WPARAM wParam, LPARAM lParam);
  26.  
  27. /* Statics */
  28.  
  29. static BOOL IsRASConnection(void);
  30. static BOOL SNTPStart(HWND hwndSNTP, const char *pServer);
  31. static void SNTPSend(HWND hwndSNTP, unsigned long serveraddr);
  32. static void SynchronizeSystemTime(HWND hwndSNTP,
  33.     DWORD seconds, DWORD fractions);
  34. static void SocketClose(HWND hwndSNTP, const char *msgbuf);
  35. static int GetServerPort(const char *buf, char *server);
  36. static void Log(HWND hwndSNTP, const char *msg);
  37. static void time2int(int *ph, int *pm, const char *src);
  38.  
  39. static char *m_section = "SNTP";
  40.  
  41. static char m_servername[BUFSIZE_SERVER] = { 0 }; // SNTP server's host name
  42. static int  m_nTimeOut = 1000;              // msec of time out
  43. static BOOL m_bSaveLog = FALSE;             // save log ?
  44. static char m_soundfile[MAX_PATH] = { 0 };  // sound file 
  45. static int  m_nMinuteDif = 0;               // forcely time difference
  46.  
  47. static BOOL  m_bSendingData = FALSE;     // now processing?
  48. static DWORD m_dwTickCountOnGetHost = 0; // starting time of getting IP address
  49. static DWORD m_dwTickCountOnSend = 0;    // starting time of sending data
  50.  
  51. static char  *m_pGetHost = NULL; // buffer of host entry
  52. static HANDLE m_hGetHost;        // task handle of WSAAsyncGetHostByName()
  53. static int    m_socket;   // socket
  54. static int    m_port;     // port
  55.  
  56. // RASAPI32.dll
  57. static HMODULE m_hRASAPI = NULL;
  58. DWORD (WINAPI *m_pRasEnumConnections)(LPRASCONN, LPDWORD, LPDWORD);
  59. DWORD (WINAPI *m_pRasGetConnectStatus)(HRASCONN, LPRASCONNSTATUS);
  60.  
  61. struct NTP_Packet {          // NTP packet
  62.     unsigned int Control_Word;
  63.     int root_delay;
  64.     int root_dispersion;
  65.     int reference_identifier;
  66.     __int64 reference_timestamp;
  67.     __int64 originate_timestamp;
  68.     __int64 receive_timestamp;
  69.     int transmit_timestamp_seconds;
  70.     int transmit_timestamp_fractions;
  71. };
  72.  
  73. /*---------------------------------------------------
  74.   initialize WinSock and read settings
  75. ---------------------------------------------------*/
  76. BOOL InitSNTP(HWND hwndMain)
  77. {
  78.     WORD ver;
  79.     WSADATA wsaData;
  80.     char s[80];
  81.     
  82.     m_socket = -1;
  83.     m_hGetHost = NULL;
  84.     
  85.     // initialize WinSock
  86.     ver = 0x0101; // MAKEWORD(1, 1);
  87.     if(WSAStartup(ver, &wsaData) != 0)
  88.     {
  89.         Log(NULL, "failed to initialize");
  90.         return FALSE;
  91.     }
  92.     
  93.     GetMyRegStr(m_section, "Server", m_servername, 80, "");
  94.     m_nTimeOut = GetMyRegLong(m_section, "Timeout", 1000);
  95.     if(!(0 < m_nTimeOut && m_nTimeOut < 30000))
  96.         m_nTimeOut = 1000;
  97.     m_bSaveLog = GetMyRegLong(m_section, "SaveLog", TRUE);
  98.     GetMyRegStr(m_section, "Sound", m_soundfile, MAX_PATH, "");
  99.     
  100.     m_nMinuteDif = 0;
  101.     GetMyRegStr(m_section, "Dif", s, 80, "");
  102.     if(s[0])
  103.     {
  104.         int h, m;
  105.         time2int(&h, &m, s);
  106.         m_nMinuteDif = h * 60 + m;
  107.     }
  108.     
  109.     return TRUE;
  110. }
  111.  
  112. /*-------------------------------------------
  113.   clean up
  114. ---------------------------------------------*/
  115. void EndSNTP(HWND hwndMain)
  116. {
  117.     SocketClose(hwndMain, NULL);
  118.     
  119.     if(m_hRASAPI) FreeLibrary(m_hRASAPI);
  120.     m_hRASAPI = NULL;
  121.     
  122.     WSACleanup();
  123. }
  124.  
  125. /*-------------------------------------------
  126.   set options
  127.   called in dialog.c
  128. ---------------------------------------------*/
  129. void SetSNTPParam(const char *servername, int nTimeOut, BOOL bLog,
  130.     const char *soundfile)
  131. {
  132.     if(servername) strcpy(m_servername, servername);
  133.     m_nTimeOut = nTimeOut;
  134.     if(0 < nTimeOut && nTimeOut < 30000)
  135.         m_nTimeOut = nTimeOut;
  136.     m_bSaveLog = bLog;
  137.     if(soundfile) strcpy(m_soundfile, soundfile);
  138. }
  139.  
  140. /*-------------------------------------------
  141.   start SNTP session
  142.   check RAS connection and call SNTPStart()
  143. ---------------------------------------------*/
  144. BOOL StartSyncTime(HWND hwnd, const char *servername, BOOL bRAS)
  145. {
  146.     if(m_socket != -1 || m_hGetHost != NULL) return FALSE;
  147.     
  148.     if(servername) strcpy(m_servername, servername);
  149.     
  150.     if(bRAS && !IsRASConnection()) return FALSE;
  151.     
  152.     return SNTPStart(hwnd, m_servername);
  153. }
  154.  
  155. /*------------------------------------------------
  156.   called when main window received WM_TIMER
  157. --------------------------------------------------*/
  158. void OnTimerSNTP(HWND hwnd)
  159. {
  160.     if(m_bSendingData) // while sending/receiving
  161.     {
  162.         char msg[80];
  163.         DWORD dif;
  164.         dif = GetTickCount() - m_dwTickCountOnSend;
  165.         if(dif >= (DWORD)m_nTimeOut)  // check timeout
  166.         {
  167.             wsprintf(msg, "timeout (%04d)", dif);
  168.             SocketClose(hwnd, msg);
  169.             PostMessage(hwnd, SNTPM_ERROR, 0, 0);
  170.             return;
  171.         }
  172.     }
  173. }
  174.  
  175. /*---------------------------------------------------
  176.   check RAS connection
  177. ---------------------------------------------------*/
  178. BOOL IsRASConnection(void)
  179. {
  180.     RASCONN rc;
  181.     RASCONNSTATUS rcs;
  182.     DWORD cb, cConnections;
  183.     
  184.     // load DLL of RAS API
  185.     
  186.     if(!m_hRASAPI && !GetMyRegLong(m_section, "NoRASAPI", FALSE))
  187.     {
  188.         m_hRASAPI = LoadLibrary("RASAPI32.dll");
  189.         if(m_hRASAPI)
  190.         {
  191.             (FARPROC)m_pRasEnumConnections =
  192.                 GetProcAddress(m_hRASAPI, "RasEnumConnectionsA");
  193.             (FARPROC)m_pRasGetConnectStatus =
  194.                 GetProcAddress(m_hRASAPI, "RasGetConnectStatusA");
  195.             if(m_pRasEnumConnections == NULL || m_pRasGetConnectStatus == NULL)
  196.             {
  197.                 FreeLibrary(m_hRASAPI); m_hRASAPI = NULL;
  198.             }
  199.         }
  200.     }
  201.     
  202.     if(!m_hRASAPI) return FALSE;
  203.     
  204.     memset(&rc, 0, sizeof(rc));
  205.     rc.dwSize = sizeof(rc);
  206.     cb = sizeof(rc);
  207.     if(m_pRasEnumConnections(&rc, &cb, &cConnections) == 0 &&
  208.         cConnections > 0)
  209.     {
  210.         memset(&rcs, 0, sizeof(rcs));
  211.         rcs.dwSize = sizeof(rcs);
  212.         if(m_pRasGetConnectStatus(rc.hrasconn, &rcs) == 0 &&
  213.             rcs.rasconnstate == RASCS_Connected) return TRUE;
  214.     }
  215.     return FALSE;
  216. }
  217.  
  218. /*---------------------------------------------------
  219.   start SNTP session
  220. ---------------------------------------------------*/
  221. BOOL SNTPStart(HWND hwndSNTP, const char *pServer)
  222. {
  223.     char servername[BUFSIZE_SERVER];
  224.     unsigned long serveraddr;
  225.     
  226.     if(m_socket != -1 || m_hGetHost != NULL) return FALSE;
  227.     
  228.     // get server name and port
  229.     m_port = GetServerPort(pServer, servername);
  230.     if(m_port == -1)
  231.     {
  232.         Log(hwndSNTP, "invalid server name"); return FALSE;
  233.     }
  234.     
  235.     // make a socket
  236.     m_socket = socket(PF_INET, SOCK_DGRAM, 0);
  237.     if(m_socket == INVALID_SOCKET)
  238.     {
  239.         Log(hwndSNTP, "socket() failed");
  240.         m_socket = -1; return FALSE;
  241.     }
  242.     
  243.     serveraddr = inet_addr(servername);
  244.     // if server name is not "XXX.XXX.XXX.XXX"
  245.     if(serveraddr == (unsigned long)-1)
  246.     {
  247.         // request IP address
  248.         m_pGetHost = malloc(MAXGETHOSTSTRUCT);
  249.         m_hGetHost = WSAAsyncGetHostByName(hwndSNTP, WSOCK_GETHOST,
  250.             servername, m_pGetHost, MAXGETHOSTSTRUCT);
  251.         if(m_hGetHost == NULL)
  252.         {
  253.             free(m_pGetHost); m_pGetHost = NULL;
  254.             SocketClose(hwndSNTP, "WSAAsyncGetHostByName() failed");
  255.             return FALSE;
  256.         }
  257.         m_dwTickCountOnGetHost = GetTickCount();
  258.         return TRUE;
  259.     }
  260.     
  261.     // send data
  262.     SNTPSend(hwndSNTP, serveraddr);
  263.     return TRUE;
  264. }
  265.  
  266. /*---------------------------------------------------
  267.   called when the window received WSOCK_GETHOST.
  268.   get IP address and send data.
  269. ---------------------------------------------------*/
  270. void OnGetHost(HWND hwndSNTP, WPARAM wParam, LPARAM lParam)
  271. {
  272.     struct hostent *pHostEnt;
  273.     unsigned long serveraddr;
  274.     
  275.     if(m_hGetHost == NULL || m_pGetHost == NULL) return;
  276.     
  277.     // valid handle ?
  278.     if(m_hGetHost != (HANDLE)wParam) return;
  279.     
  280.     // success ?
  281.     if(WSAGETASYNCERROR(lParam) != 0)
  282.     {
  283.         SocketClose(hwndSNTP, "failed to get IP address");
  284.         PostMessage(hwndSNTP, SNTPM_ERROR, 0, 0);
  285.         return;
  286.     }
  287.     
  288.     // get IP address
  289.     pHostEnt = (struct hostent *)m_pGetHost;
  290.     serveraddr =  *((unsigned long *)((pHostEnt->h_addr_list)[0]));
  291.     free(m_pGetHost); m_pGetHost = NULL;
  292.     m_hGetHost = NULL;
  293.     
  294.     // send data
  295.     SNTPSend(hwndSNTP, serveraddr);
  296. }
  297.  
  298. /*---------------------------------------------------
  299.   send SNTP data
  300. ---------------------------------------------------*/
  301. void SNTPSend(HWND hwndSNTP, unsigned long serveraddr)
  302. {
  303.     struct sockaddr_in serversockaddr;
  304.     struct NTP_Packet NTP_Send;
  305.     unsigned int sntpver;
  306.     unsigned int Control_Word;
  307.     
  308.     // request notification of events
  309.     if(WSAAsyncSelect(m_socket, hwndSNTP, WSOCK_SELECT, FD_READ)
  310.         == SOCKET_ERROR)
  311.     {
  312.         SocketClose(hwndSNTP, "WSAAsyncSelect() failed");
  313.         PostMessage(hwndSNTP, SNTPM_ERROR, 0, 0);
  314.         return;
  315.     }
  316.     
  317.     // set IP address and port
  318.     serversockaddr.sin_family = AF_INET;
  319.     serversockaddr.sin_addr.s_addr = serveraddr;
  320.     serversockaddr.sin_port = htons((unsigned short)m_port);
  321.     memset(serversockaddr.sin_zero,(int)0,sizeof(serversockaddr.sin_zero));
  322.     
  323.     
  324.     // init a packet
  325.     memset(&NTP_Send, 0, sizeof(struct NTP_Packet));
  326.     // NTP/SNTP version number = 4
  327.     // Mode = 3 (client)
  328.     // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  329.     // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  330.     // |LI | VN  |Mode |    Stratum    |     Poll      |   Precision   |
  331.     // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  332.     sntpver = GetMyRegLong(m_section, "SNTPVer", 4);
  333.     Control_Word = (sntpver << 27) | (3 << 24);
  334.     NTP_Send.Control_Word = htonl(Control_Word);
  335.     
  336.     // send a packet
  337.     if(sendto(m_socket, (const char *)&NTP_Send, sizeof(NTP_Send), 0, 
  338.         (struct sockaddr *)&serversockaddr,
  339.         sizeof(serversockaddr)) == SOCKET_ERROR)
  340.     {
  341.         SocketClose(hwndSNTP, "sendto() failed");
  342.         PostMessage(hwndSNTP, SNTPM_ERROR, 0, 0);
  343.         return;
  344.     }
  345.     
  346.     // save tickcount
  347.     m_dwTickCountOnSend = GetTickCount();
  348.     m_bSendingData = TRUE;
  349. }
  350.  
  351. /*---------------------------------------------------
  352.   called when the window received WSOCK_SELECT.
  353.   receive SNTP data and set time.
  354. ---------------------------------------------------*/
  355. void OnReceive(HWND hwndSNTP, WPARAM wParam, LPARAM lParam)
  356. {
  357.     struct sockaddr_in serversockaddr;
  358.     struct NTP_Packet NTP_Recv;
  359.     int sockaddr_Size;
  360.     
  361.     if(m_socket == -1) return;
  362.     if(WSAGETSELECTERROR(lParam))
  363.     {
  364.         SocketClose(hwndSNTP, "failed to receive");
  365.         return;
  366.     }
  367.     if(m_socket != (int)wParam ||
  368.         WSAGETSELECTEVENT(lParam) != FD_READ) return;
  369.     
  370.     // receive data
  371.     sockaddr_Size = sizeof(serversockaddr);
  372.     if(recvfrom(m_socket, (char *)&NTP_Recv, sizeof(NTP_Recv), 0,
  373.         (struct sockaddr *)&serversockaddr, &sockaddr_Size) == SOCKET_ERROR)
  374.     {
  375.         SocketClose(hwndSNTP, "recvfrom() failed");
  376.         return;
  377.     }
  378.     
  379.     // if Leap Indicator is 3
  380.     /*
  381.     if(ntohl(NTP_Recv.Control_Word) >> 30 == 3)
  382.     {
  383.         SocketClose(hwndSNTP, "server is unhealthy");
  384.         return;
  385.     }
  386.     */
  387.     
  388.     // set system time
  389.     SynchronizeSystemTime(hwndSNTP,
  390.         ntohl(NTP_Recv.transmit_timestamp_seconds),
  391.         ntohl(NTP_Recv.transmit_timestamp_fractions));
  392.     
  393.     // close socket
  394.     SocketClose(hwndSNTP, NULL);
  395. }
  396.  
  397. /*---------------------------------------------------
  398.   set system time to received data
  399. ---------------------------------------------------*/
  400. void SynchronizeSystemTime(HWND hwndSNTP, DWORD seconds, DWORD fractions)
  401. {
  402.     FILETIME ft, ftold;
  403.     SYSTEMTIME st, st_dif;
  404.     char s[MAX_PATH];
  405.     DWORD sr_time;
  406.     DWORDLONG dif;
  407.     BOOL b;
  408.     
  409.     // timeout ?
  410.     sr_time = GetTickCount() - m_dwTickCountOnSend;
  411.     if(sr_time >= (DWORD)m_nTimeOut)
  412.     {
  413.         wsprintf(s, "timeout (%04d)", sr_time);
  414.         Log(hwndSNTP, s);
  415.         PostMessage(hwndSNTP, SNTPM_ERROR, 0, 0);
  416.         return;
  417.     }
  418.     
  419.     // current time
  420.     GetSystemTimeAsFileTime(&ftold);
  421.     
  422.     // NTP data -> FILETIME
  423.     *(DWORDLONG*)&ft =
  424.         // seconds from 1900/01/01 ü¿ 100 nano-seconds from 1601/01/01
  425.         M32x32to64(seconds, 10000000) + 94354848000000000i64;
  426.     
  427.     // difference
  428.     if(m_nMinuteDif > 0)
  429.         *(DWORDLONG*)&ft += M32x32to64(m_nMinuteDif * 60, 10000000);
  430.     else if(m_nMinuteDif < 0)
  431.         *(DWORDLONG*)&ft -= M32x32to64(-m_nMinuteDif * 60, 10000000);
  432.     
  433.     // set system time
  434.     b = FileTimeToSystemTime(&ft, &st);
  435.     if(b)
  436.     {
  437.         /* fractions: (2 ** 32 / 1000) */
  438.         st.wMilliseconds = (WORD)(fractions / 4294967);
  439.         b = SetSystemTime(&st);
  440.     }
  441.     if(!b)
  442.     {
  443.         Log(hwndSNTP, "failed to set time");
  444.         PostMessage(hwndSNTP, SNTPM_ERROR, 0, 0);
  445.         return;
  446.     }
  447.     
  448.     SystemTimeToFileTime(&st, &ft);
  449.     // delayed or advanced
  450.     b = (*(DWORDLONG*)&ft > *(DWORDLONG*)&ftold);
  451.     // get difference
  452.     if(b) dif = *(DWORDLONG*)&ft - *(DWORDLONG*)&ftold;
  453.     else  dif = *(DWORDLONG*)&ftold - *(DWORDLONG*)&ft;
  454.     FileTimeToSystemTime((FILETIME*)&dif, &st_dif);
  455.     
  456.     // save log
  457.     strcpy(s, "synchronized ");
  458.     if(st_dif.wYear == 1601 && st_dif.wMonth == 1 &&
  459.         st_dif.wDay == 1 && st_dif.wHour == 0)
  460.     {
  461.         strcat(s, b?"+":"-");
  462.         wsprintf(s + strlen(s), "%02d:%02d.%03d ",
  463.             st_dif.wMinute, st_dif.wSecond, st_dif.wMilliseconds);
  464.     }
  465.     wsprintf(s + strlen(s), "(%04d)", sr_time);
  466.     Log(hwndSNTP, s);
  467.     
  468.     if(m_soundfile[0])
  469.     {
  470.         HWND hwndTClockMain = GetTClockMainWindow();
  471.         
  472.         if(hwndTClockMain)
  473.             SendStringToOther(hwndTClockMain, hwndSNTP,
  474.                 m_soundfile, COPYDATA_SOUND);
  475.     }
  476.     
  477.     PostMessage(hwndSNTP, SNTPM_SUCCESS, 0, 0);
  478. }
  479.  
  480. /*---------------------------------------------------
  481.   close the socket
  482. ---------------------------------------------------*/
  483. void SocketClose(HWND hwndSNTP, const char *msgbuf)
  484. {
  485.     if(!hwndSNTP) return;
  486.     
  487.     // cancel task handle of WSAAsyncGetHostByName()
  488.     if(m_hGetHost != NULL) WSACancelAsyncRequest(m_hGetHost);
  489.     m_hGetHost = NULL;
  490.     // free memory
  491.     if(m_pGetHost) free(m_pGetHost);
  492.     m_pGetHost = NULL;
  493.     
  494.     if(m_socket != -1)
  495.     {
  496.         // cancel request of notification
  497.         WSAAsyncSelect(m_socket, hwndSNTP, 0, 0);
  498.         // close socket
  499.         closesocket(m_socket);
  500.     }
  501.     m_socket = -1;
  502.     m_bSendingData = FALSE;
  503.     
  504.     if(msgbuf) Log(hwndSNTP, msgbuf);
  505. }
  506.  
  507. /*---------------------------------------------------
  508.     get server name and port number from string
  509.         buf: "ntp.xxxxx.ac.jp:123"
  510. ---------------------------------------------------*/
  511. int GetServerPort(const char *buf, char *server)
  512. {
  513.     char *p;
  514.     int port = 123;
  515.     
  516.     if(strcmp(buf, "") == 0) return -1;
  517.     strcpy(server, buf);
  518.     
  519.     for(p = server; *p != ':' && *p != '\0'; p++);
  520.     if(*p == ':')
  521.     {
  522.         *p = 0; p++; port = 0;
  523.         while(*p)
  524.         {
  525.             if('0' <= *p && *p <= '9')
  526.                 port = port * 10 + *p - '0';
  527.             else
  528.             {
  529.                 port = -1; break;
  530.             }
  531.             p++;
  532.         }
  533.     }
  534.     return port;
  535. }
  536.  
  537. /*-------------------------------------------
  538.     save log data
  539. ---------------------------------------------*/
  540. void Log(HWND hwndSNTP, const char *msg)
  541. {
  542.     SYSTEMTIME st;
  543.     char s[160];
  544.     int pos;
  545.     
  546.     GetLocalTime(&st);
  547.     wsprintf(s, "%02d/%02d %02d:%02d:%02d ",
  548.         st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
  549.     strcat(s, msg);
  550.     strcat(s, "\r\n");
  551.     
  552.     // save to edit control
  553.     if(g_hwndLog)
  554.     {
  555.         pos = SendMessage(g_hwndLog, WM_GETTEXTLENGTH, 0, 0);
  556.         SendMessage(g_hwndLog, EM_SETSEL, pos, pos);
  557.         SendMessage(g_hwndLog, EM_REPLACESEL, 0, (LPARAM)s);
  558.     }
  559.     
  560.     // save to file
  561.     if(m_bSaveLog)
  562.     {
  563.         HFILE hf;
  564.         char fname[MAX_PATH];
  565.         
  566.         strcpy(fname, g_mydir);
  567.         add_title(fname, "SNTP.txt");
  568.         hf = _lopen(fname, OF_WRITE);
  569.         if(hf == HFILE_ERROR)
  570.             hf = _lcreat(fname, 0);
  571.         if(hf == HFILE_ERROR) return;
  572.         _llseek(hf, 0, 2);
  573.         _lwrite(hf, s, strlen(s));
  574.         _lclose(hf);
  575.     }
  576. }
  577.  
  578. /*-------------------------------------------
  579.     "XX:XX" -> two integers
  580. ---------------------------------------------*/
  581. void time2int(int *ph, int *pm, const char *src)
  582. {
  583.     const char *p;
  584.     BOOL bminus;
  585.     
  586.     p = src;
  587.     *ph = 0; *pm = 0;
  588.     bminus = FALSE;
  589.     if(*p == '-') { p++; bminus = TRUE; }
  590.     while('0' <= *p && *p <='9')
  591.         *ph = *ph * 10 + *p++ - '0';
  592.     if(bminus) *ph *= -1;
  593.     if(*p == ':') p++; else return;
  594.     while('0' <= *p && *p <='9')
  595.         *pm = *pm * 10 + *p++ - '0';
  596.     if(bminus) *pm *= -1;
  597. }
  598.